/**
 * Ymodem协议实现
 * 用于通过蓝牙进行固件升级
 * 参考标准：Ymodem Protocol Specification
 */

// 控制字符
const SOH = 0x01;  // 128字节数据包头
const STX = 0x02;  // 1024字节数据包头
const EOT = 0x04;  // 传输结束
const ACK = 0x06;  // 确认
const NAK = 0x15;  // 否认
const CAN = 0x18;  // 取消
const CRC = 0x43;  // 'C' - 请求CRC校验
const A1  = 0x41;  // 'A' - 中止信号1
const A2  = 0x61;  // 'a' - 中止信号2

class YmodemProtocol {
  constructor(options = {}) {
    this.packetSize = options.packetSize || 128; // 128 或 1024
    this.timeout = options.timeout || 3000;
    this.maxRetry = options.maxRetry || 10;
    this.onProgress = options.onProgress || (() => {});
    this.onError = options.onError || (() => {});
  }

  /**
   * CRC16-CCITT校验
   */
  crc16(data) {
    let crc = 0;
    for (let i = 0; i < data.length; i++) {
      crc = crc ^ (data[i] << 8);
      for (let j = 0; j < 8; j++) {
        if (crc & 0x8000) {
          crc = (crc << 1) ^ 0x1021;
        } else {
          crc = crc << 1;
        }
      }
    }
    return crc & 0xFFFF;
  }

  /**
   * 构建Ymodem数据包
   * @param {number} seq - 序列号
   * @param {Uint8Array} data - 数据
   */
  buildPacket(seq, data) {
    const header = this.packetSize === 1024 ? STX : SOH;
    const dataSize = this.packetSize;
    const packet = new Uint8Array(dataSize + 5); // header(1) + seq(1) + ~seq(1) + data + crc(2)

    packet[0] = header;
    packet[1] = seq & 0xFF;
    packet[2] = (~seq) & 0xFF;

    // 填充数据（根据C++代码，使用0xFF填充）
    for (let i = 0; i < dataSize; i++) {
      packet[3 + i] = i < data.length ? data[i] : 0xFF; // 不足部分填充0xFF
    }

    // 计算CRC
    const crc = this.crc16(packet.slice(3, 3 + dataSize));
    packet[3 + dataSize] = (crc >> 8) & 0xFF;
    packet[4 + dataSize] = crc & 0xFF;

    return packet;
  }

  /**
   * 构建文件信息包（第0包）
   * @param {string} filename - 文件名
   * @param {number} filesize - 文件大小
   */
  buildFileInfoPacket(filename, filesize) {
    const data = new Uint8Array(128);
    const nameBytes = this.stringToBytes(filename);
    const sizeStr = filesize.toString();
    const sizeBytes = this.stringToBytes(sizeStr);

    // 文件名
    let offset = 0;
    for (let i = 0; i < nameBytes.length && offset < 100; i++) {
      data[offset++] = nameBytes[i];
    }
    data[offset++] = 0; // NULL终止符

    // 文件大小
    for (let i = 0; i < sizeBytes.length && offset < 128; i++) {
      data[offset++] = sizeBytes[i];
    }

    return this.buildPacket(0, data);
  }

  /**
   * 字符串转字节数组
   */
  stringToBytes(str) {
    const bytes = new Uint8Array(str.length);
    for (let i = 0; i < str.length; i++) {
      bytes[i] = str.charCodeAt(i) & 0xFF;
    }
    return bytes;
  }

  /**
   * 发送文件（主函数）
   * @param {Uint8Array} fileData - 文件数据
   * @param {string} filename - 文件名
   * @param {Function} sendFunc - 发送函数
   * @param {Function} receiveFunc - 接收函数
   */
  async sendFile(fileData, filename, sendFunc, receiveFunc) {
    try {
      console.log('[Ymodem] 开始传输, 文件:', filename, '大小:', fileData.length);

      // 阶段1：等待接收端发送'C'
      console.log('[Ymodem] 等待接收端准备...');
      const startSignal = await this.waitForSignal(receiveFunc, CRC, 10000);
      if (!startSignal) {
        throw new Error('接收端无响应，请确保控制器已进入升级模式');
      }

      // 阶段2：发送文件信息包（第0包）
      console.log('[Ymodem] 发送文件信息...');
      const fileInfoPacket = this.buildFileInfoPacket(filename, fileData.length);
      await this.sendPacketWithRetry(fileInfoPacket, sendFunc, receiveFunc);

      // 等待接收端准备接收数据
      await this.waitForSignal(receiveFunc, CRC, 5000);

      // 阶段3：发送数据包
      console.log('[Ymodem] 开始发送数据包...');
      const totalPackets = Math.ceil(fileData.length / this.packetSize);
      let offset = 0;
      let seq = 1;

      for (let i = 0; i < totalPackets; i++) {
        const chunk = fileData.slice(offset, offset + this.packetSize);
        const packet = this.buildPacket(seq, chunk);
        
        await this.sendPacketWithRetry(packet, sendFunc, receiveFunc);
        
        offset += this.packetSize;
        seq = (seq + 1) & 0xFF; // 序列号循环（0-255）

        // 进度回调
        this.onProgress({
          current: i + 1,
          total: totalPackets,
          percentage: ((i + 1) / totalPackets * 100).toFixed(2)
        });
      }

      // 阶段4：发送EOT
      console.log('[Ymodem] 发送传输结束信号...');
      await this.sendEOT(sendFunc, receiveFunc);

      // 阶段5：发送空包结束会话
      await this.waitForSignal(receiveFunc, CRC, 5000);
      const emptyPacket = this.buildPacket(0, new Uint8Array(128));
      await sendFunc(emptyPacket);

      console.log('[Ymodem] 传输完成！');
      return true;

    } catch (error) {
      console.error('[Ymodem] 传输失败:', error);
      this.onError(error);
      return false;
    }
  }

  /**
   * 发送数据包（带重试）
   */
  async sendPacketWithRetry(packet, sendFunc, receiveFunc) {
    for (let retry = 0; retry < this.maxRetry; retry++) {
      await sendFunc(packet);
      
      const response = await this.waitForResponse(receiveFunc, this.timeout);
      
      if (response === ACK) {
        return true;
      } else if (response === NAK) {
        console.log('[Ymodem] 收到NAK，重试...', retry + 1);
        continue;
      } else if (response === CAN) {
        throw new Error('传输被接收端取消');
      } else {
        console.log('[Ymodem] 无效响应，重试...', retry + 1);
      }
    }
    throw new Error('发送数据包失败，超过最大重试次数');
  }

  /**
   * 发送EOT
   */
  async sendEOT(sendFunc, receiveFunc) {
    const eot = new Uint8Array([EOT]);
    
    for (let retry = 0; retry < 5; retry++) {
      await sendFunc(eot);
      const response = await this.waitForResponse(receiveFunc, this.timeout);
      
      if (response === ACK) {
        return true;
      } else if (response === NAK) {
        console.log('[Ymodem] EOT收到NAK，重试...', retry + 1);
        continue;
      }
    }
    throw new Error('发送EOT失败');
  }

  /**
   * 等待特定信号
   */
  async waitForSignal(receiveFunc, expectedSignal, timeout) {
    console.log('[Ymodem] 等待信号:', expectedSignal.toString(16), '超时时间:', timeout + 'ms');
    const startTime = Date.now();
    let checkCount = 0;
    
    while (true) {
      checkCount++;
      
      // 调用receiveFunc获取数据（async方式，不清空队列）
      const data = await receiveFunc(false);
      
      if (data && data.length > 0) {
        console.log('[Ymodem] 第', checkCount, '次检查，收到数据:', Array.from(data), '期望:', expectedSignal.toString(16));
        
        // 检查是否有期望的信号
        for (let i = 0; i < data.length; i++) {
          if (data[i] === expectedSignal) {
            console.log('[Ymodem] 信号匹配成功！位置:', i);
            // 清空队列
            await receiveFunc(true);
            return true;
          }
        }
      }
      
      // 检查超时
      const elapsed = Date.now() - startTime;
      if (elapsed > timeout) {
        console.error('[Ymodem] 等待信号超时！已等待:', elapsed + 'ms', '检查次数:', checkCount);
        return false;
      }
      
      // 等待一段时间再检查
      await new Promise(resolve => setTimeout(resolve, 100));
    }
  }

  /**
   * 等待响应
   */
  async waitForResponse(receiveFunc, timeout) {
    const startTime = Date.now();
    let checkCount = 0;
    
    while (true) {
      checkCount++;
      
      // 调用receiveFunc获取数据（async方式，不清空队列）
      const data = await receiveFunc(false);
      
      if (data && data.length > 0) {
        console.log('[Ymodem] waitForResponse收到数据(第', checkCount, '次):', Array.from(data));
        
        // 在数据中查找有效的响应字符（ACK, NAK, CAN, A1, A2）
        for (let i = 0; i < data.length; i++) {
          if (data[i] === ACK || data[i] === NAK || data[i] === CAN || data[i] === A1 || data[i] === A2) {
            const typeName = data[i] === ACK ? 'ACK' : 
                            data[i] === NAK ? 'NAK' : 
                            data[i] === CAN ? 'CAN' : 
                            data[i] === A1 ? 'ABORT(A1)' : 'ABORT(A2)';
            console.log('[Ymodem] 找到响应:', data[i].toString(16), '=', typeName);
            
            if (data[i] === A1 || data[i] === A2) {
              console.error('[Ymodem] 控制器发送中止信号！可能是数据包校验失败或格式错误');
            }
            
            // 清空队列
            await receiveFunc(true);
            return data[i];
          }
        }
        
        // 如果是0，可能是填充字符，继续等待
        if (data[0] !== 0) {
          // 非0且非ACK/NAK/CAN，可能是异常数据
          console.log('[Ymodem] 未找到有效响应，收到:', data[0].toString(16));
          // 清空队列
          await receiveFunc(true);
          return data[0];
        }
      }
      
      // 检查超时
      const elapsed = Date.now() - startTime;
      if (elapsed > timeout) {
        console.error('[Ymodem] waitForResponse超时！已等待:', elapsed + 'ms', '检查次数:', checkCount);
        return null;
      }
      
      // 等待一段时间再检查
      await new Promise(resolve => setTimeout(resolve, 50));
    }
  }
}

module.exports = YmodemProtocol;

